#! -*- coding: utf-8 -*-
"""
    常用功能函数
    字符串处理，时间格式处理，或者类型转换等操作
"""
import math
import struct
import types
import re

from bson import ObjectId, objectid
from bson.errors import InvalidId
from Crypto.Cipher import ARC4


def valid_object_id(oid):
    if not oid:
        return False
    try:
        _ = objectid.ObjectId(oid)
    except (objectid.InvalidId, TypeError) as e:
        return False
    else:
        return True


def get_objectId(oid):
    try:
        return ObjectId(oid) if oid is not None else None
    except (InvalidId, TypeError):
        return None


def trim(s):
    return s.strip(' \t\n\r') if s else ''


def safe_float(value, default=0):
    """
    Turn value into float type, return 0 when exception happens
    :param value: value for transforming
    :param default: 0
    :return: value of float type
    """
    try:
        value = float(value)
    except:
        value = default
    return value


def safe_int(value, default=0):
    """
    Turn value into int type, return 0 when exception happens
    :param value: value for transforming
    :param default: 0
    :return: value of int type
    """
    try:
        value = int(value)
    except:
        value = default
    return value


def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a bytestring version of 's', encoded as specified in 'encoding'.
    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (types.NoneType, int)):
        return s
    elif not isinstance(s, basestring):
        try:
            return str(s)
        except UnicodeEncodeError:
            if isinstance(s, Exception):
                # An Exception subclass containing non-ASCII data that doesn't
                # know how to print itself properly. We shouldn't raise a
                # further exception.
                return ' '.join([smart_str(arg, encoding, strings_only,
                                           errors) for arg in s])
            return unicode(s).encode(encoding, errors)
    elif isinstance(s, unicode):
        return s.encode(encoding, errors)
    elif s and encoding != 'utf-8':
        return s.decode('utf-8', errors).encode(encoding, errors)
    else:
        return s


class IdCipher(object):
    secret_key = 'duzIteng'
    MAX = 4294967295

    def encrypt(self, id):
        obj = ARC4.new(self.secret_key)
        ciph = obj.encrypt(struct.pack('I', id))
        return struct.unpack('I', ciph)[0]

    def decrypt(self, ciph):
        obj = ARC4.new(self.secret_key)
        id = obj.decrypt(struct.pack('I', ciph))
        return struct.unpack('I', id)[0]


id_cipher = IdCipher()
id_encrypt = id_cipher.encrypt
id_decrypt = id_cipher.decrypt


def points2distance(origin, destination):
    """
    Caculate the distance between two points(in km)
    """
    #    lat1, lon1 = origin
    #    lat2, lon2 = destination
    lon1, lat1 = origin
    lon2, lat2 = destination

    radius = 6371  # km

    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lon2 - lon1)
    a = math.sin(dlat / 2) * math.sin(dlat / 2) + math.cos(math.radians(lat1)) \
                                                  * math.cos(math.radians(lat2)) * math.sin(dlon / 2) * math.sin(
        dlon / 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = radius * c

    return d


if type('') is not type(b''):
    def u(s):
        return s

    bytes_type = bytes
    unicode_type = str
    basestring_type = str
else:
    def u(s):
        return s.decode('unicode_escape')

    bytes_type = str
    unicode_type = unicode

_TO_UNICODE_TYPES = (unicode_type, type(None))


def to_unicode(value):
    """Converts a string argument to a unicode string.

    If the argument is already a unicode string or None, it is returned
    unchanged.  Otherwise it must be a byte string and is decoded as utf8.
    """
    if isinstance(value, _TO_UNICODE_TYPES):
        return value
    if not isinstance(value, bytes_type):
        raise TypeError(
            "Expected bytes, unicode, or None; got %r" % type(value)
        )
    return value.decode("utf-8")


def filter_text(text):
    """过滤text中的特殊符号，返回过滤后的内容"""
    if not text:
        return ""
    res = re.findall(u"[\u4e00-\u9fa5 \d \w]+", to_unicode(text))
    return " ".join("".join(res).split()) if res else ""


def allowed_file(filename):
    from app.constants import ALLOWED_EXTENSIONS

    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS


def check_object_ids(ids):
    for id in ids:
        if not get_objectId(id):
            return False
    return True


def compressed_dict(raw, keys):
    """Compress a dict"""
    return dict((k, v) for k, v in raw.iteritems() if k in keys)
